/* Copyright 2008 Bas van den Berg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <string>

#include "yaffut.h"
#include "myassert.h"
#include "TestRoom.h"
#include "TestSession.h"
#include "Global.h"

#include "TaskQueue.h"
#include "Area.h"
#include "MudPlayer.h"
#include "Error.h"
#include "config.h"

#define private public
#include "MudExit.h"
#include "ElevatorController.h"
#undef private

using namespace std;
using namespace Chronos;

class ElevatorControllerTest {
public:
    ElevatorControllerTest()
        : taskQueue(0)
		, area(0, "")
        , inExit(new MudRoomExit("elevator", "", "arrives", "leaves", "enter", area, 1))
        , elevator(new TestRoom("Inside elevator"))
        , floor1(new TestRoom("floor1"))
        , floor2(new TestRoom("floor2"))
        , floor3(new TestRoom("floor3"))
		, controller(*elevator, inExit, area)
		, session("player")
		, player (&session.getPlayer())
    {
        area.addRoom(1, *elevator);
        area.addRoom(2, *floor1);
        area.addRoom(3, *floor2);
        area.addRoom(4, *floor3);
        controller.addFloor(1, 2);
        controller.addFloor(2, 3);
        controller.addFloor(3, 4);
		Global<TaskQueue>::set(&taskQueue);
    }
    void waitTime(unsigned int delay) {
        for (unsigned int i=0; i<delay; ++i) taskQueue.runNextTime();
    }

    TaskQueue taskQueue;
    Area area;
    MudRoomExit* inExit;
    TestRoom* elevator;
    TestRoom* floor1;
    TestRoom* floor2;
    TestRoom* floor3;
	ElevatorController controller;
	TestSession session;
	MudPlayer* player;
};


TEST(ElevatorControllerTest, testRequestingCurrentFloor) {
	controller.request(*player, 1);
	session.SESSION_CHECKWRITTEN("");
	floor1->ROOM_CHECKWRITTEN("The elevator doors open.\n");
	elevator->ROOM_CHECKWRITTEN("The doors open.\n");
    ASSERT_NOT_NULL(elevator->findObject("out"));
    ASSERT_NOT_NULL(floor1->findObject("elevator"));

	controller.request(*player, 1);
	session.SESSION_CHECKWRITTEN("The doors are already open.\n");
	floor1->ROOM_CHECKWRITTEN("");

	waitTime(seconds(ElevatorController::ELEVATOR_OPEN_TIME));
	floor1->ROOM_CHECKWRITTEN("The elevator doors close.\n");
	elevator->ROOM_CHECKWRITTEN("The doors close.\n");
    ASSERT_NULL(elevator->findObject("out"));
    ASSERT_NULL(floor1->findObject("elevator"));
}


TEST(ElevatorControllerTest, testRequestingUnknownFloor) {
	try {
		controller.request(*player, 4);
		ASSERT_FAIL();
	} catch (MudError& e) {
        ASSERT_STR_EQUAL("ElevatorController: unknown floor", e.what());
	}
}


TEST(ElevatorControllerTest, testRequestingOtherFloor) {
	controller.request(*player, 3);
	elevator->ROOM_CHECKWRITTEN("The elevator starts moving up.\n");
	floor1->ROOM_CHECKWRITTEN("");
    ASSERT_NULL(elevator->findObject("out"));
    ASSERT_NULL(floor1->findObject("elevator"));

	controller.run();
	elevator->ROOM_CHECKWRITTEN("The elevator passes floor 2.\n");

	controller.run();
	elevator->ROOM_CHECKWRITTEN("The elevator stops on floor 3.\nThe doors open.\n");
	floor3->ROOM_CHECKWRITTEN("The elevator arrives.\nThe elevator doors open.\n");
    ASSERT_NOT_NULL(elevator->findObject("out"));
    ASSERT_NOT_NULL(floor3->findObject("elevator"));
}

